package com.hao.study.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * IP工具类，用于获取客户端IP地址。
 * 由于在通过代理服务器转发请求的情况下，直接通过request.getRemoteAddr()获取的IP地址可能是代理服务器的IP，
 * 因此本类提供了方法来尝试从请求头中获取真实的客户端IP地址。
 */
public class IpUtils {
    private static final Logger logger = LoggerFactory.getLogger(IpUtils.class);

    /**
     * 从HttpServletRequest中获取客户端IP地址。
     * 尝试从多个请求头中获取IP地址，这些头信息包括但不限于：
     * X-Forwarded-For: 用于通过HTTP代理或负载平衡器转发的请求；
     * Proxy-Client-IP: 用于通过HTTP代理转发的请求；
     * WL-Proxy-Client-IP: WebLogic服务器专用的代理IP头；
     * HTTP_CLIENT_IP: 一些代理服务器使用的非标准头；
     * HTTP_X_FORWARDED_FOR: 与X-Forwarded-For类似，用于通过HTTP代理或负载平衡器转发的请求。
     * 如果无法从请求头中获取到IP地址，则退回到使用request.getRemoteAddr()方法获取IP地址。
     *
     * @param request HttpServletRequest对象，用于获取请求头信息和客户端IP地址。
     * @return 客户端IP地址字符串，如果无法获取到则返回null。
     */
    /**
     * 获取IP地址
     *
     * 使用Nginx等反向代理软件， 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，X-Forwarded-For中第一个非unknown的有效IP字符串，则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String unknown = "unknown";
        String ip = null;
        try {
            // 尝试从X-Forwarded-For头中获取IP地址，这是一个常见的代理头信息。
            ip = request.getHeader("x-forwarded-for");
            // 如果IP地址为空、未知或不合法，则尝试从其他请求头中获取。
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            // 如果所有尝试都未能获取到有效的IP地址，则退回到使用默认方法获取。
            if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            // 记录获取IP地址过程中可能出现的异常。
            logger.error("IPUtils ERROR ", e);
        }

        return ip;
    }
}

